﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Algobox.JuicyLib.Apps.Sterling
{
    public class SterlingDailyStockFile
    {
        public SterlingDailyStockFile(SterlingTradeImporter2 importer)
        {
            Importer = importer;
        }

        SterlingTradeImporter2 Importer { get; set; }

        const char SEPERATOR_COL = ',';
        const int COLUMNS = 46;
        const int IX_ACCOUNT = 2;
        const int IX_SIDE = 3;
        const int IX_SYMBOL = 4;
        const int IX_QUANTITY = 5;
        const int IX_PRICE = 6;
        const int IX_DEST = 7;
        const int IX_TIME = 9;
        const int IX_CUSIP = 11;
        const int IX_FLAG = 12;
        const int IX_ORDERID = 13;
        const int IX_BROKER = 14;
        const int IX_RECORDID = 39;
        const int IX_FLAGNATIVE = 44;
        const int IX_CCY = 45;
        const int IX_CPTY = 46;
        const int IX_ORDERTIME = 16;
        const int IX_SETTLEMENTDATE = 26;

        public bool ImportStockFile(string filename, string data, List<string> ignoredAccounts)
        {
            int countMissing = 0;
            int countSuccess = 0;

            string archiveDir = String.Empty;
            DateTime fileDate = DateTime.Now.Date;

            try
            {
                // get file date
                archiveDir = Path.GetFileNameWithoutExtension(filename);
                archiveDir = archiveDir.Substring(archiveDir.Length - 8);
                fileDate = DateTime.ParseExact(archiveDir, "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None);
            }
            catch
            {
                JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed to parse date from '{0}' in filename '{1}' so using {2}", archiveDir, filename, fileDate);
                Console.WriteLine("Failed to parse date from '{0}' in filename '{1}' so using {2}", archiveDir, filename, fileDate);
            }

            // get the location to archive the file
            archiveDir = Importer.ArchiveDirectory + fileDate.ToString("yyyy/MM/dd") + "/";
            
            List<string> tradeKeys = null;
            try
            {
                // try and load all trade identifiers for this trade date to insure we don't import dupes
                tradeKeys = Importer.LiquidViewLinq.Trades.Where(x => x.Host == Importer.HostName && x.TradeTime > fileDate && x.TradeTime < fileDate.AddDays(1)).Select(t => t.ExchangeOrderId + t.ExchangeTradeId).ToList();
            }
            catch (Exception e)
            {
                JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed to load trade identifiers for host {0}, cannot continue with this file. Exception({1})", Importer.HostName, e);
                Console.WriteLine("Failed to load trade identifiers for host {0}, cannot continue with this file. Exception({1})", Importer.HostName, e);
                return false;
            }
            
            try
            {
                var query = from csvline in data.Split('\n')                            
                            let csv = csvline.Split(SEPERATOR_COL)
                            where csv.Length >= COLUMNS
                            select new
                            {
                                Account = csv[IX_ACCOUNT],
                                Side = csv[IX_SIDE],
                                Symbol = csv[IX_SYMBOL],
                                Quantity = csv[IX_QUANTITY],
                                Price = csv[IX_PRICE],
                                Destination = csv[IX_DEST],
                                TradeTime = csv[IX_TIME],
                                Cusip = csv[IX_CUSIP],
                                Flag = csv[IX_FLAG],
                                FlagNative = csv[IX_FLAGNATIVE],
                                OrderId = csv[IX_ORDERID],
                                BrokerCode = csv[IX_BROKER],
                                RecordId = csv[IX_RECORDID],
                                Counterparty = csv[IX_CPTY],
                                Currency = csv[IX_CCY],
                                OrderTime = csv[IX_ORDERTIME],
                                SettlementDate = csv[IX_SETTLEMENTDATE]
                            };

                string exchange, orderId, tradeId, account, routedMIC, flag, currency, broker;
                DateTime tradeTime, orderTime, settlementDate;
                long quantity;
                decimal price;
                int index;
                bool isAggressor;

                var enumerableQuery = query.GetEnumerator();
                for(int i = 0; enumerableQuery.MoveNext(); i++)
                {
                    if (i == 0)
                        continue;

                    var row = enumerableQuery.Current;
                    
                    try
                    {
                        account = row.Account;
                        orderId = row.OrderId;
                        currency = row.Currency;
                        index = orderId.IndexOf('-');
                        tradeId = row.RecordId + orderId.Substring(index, orderId.Length - index);
                        orderId = "E-" + account + "-" + row.OrderId;

                        // if trade exists then continue
                        if (tradeKeys.Contains(orderId + tradeId))
                            continue;

                        if (ignoredAccounts.Contains(account))
                            continue;

                        ++countMissing;

                        // quantity
                        if (!Int64.TryParse(row.Quantity, out quantity))
                        {
                            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed to parse quantity for record Account({0}) Symbol({1}) Quantity({2}) Price({3}) TradeTime({4}) OrderId({5}) TradeId({6})", account, row.Symbol, row.Quantity, row.Price, row.TradeTime, orderId, tradeId);
                            Console.WriteLine("Can't parse quantity:  " + row.Quantity);
                            continue;
                        }

                        // quantity side
                        if (row.Side != "B")
                            quantity = -quantity;

                        // price
                        if (!Decimal.TryParse(row.Price, out price))
                        {
                            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed to parse price for record Account({0}) Symbol({1}) Quantity({2}) Price({3}) TradeTime({4}) OrderId({5}) TradeId({6})", account, row.Symbol, row.Quantity, row.Price, row.TradeTime, orderId, tradeId);
                            Console.WriteLine("Can't parse price:  " + row.Price);
                            continue;
                        }

                        // trade time
                        if (!DateTime.TryParseExact(row.TradeTime,
                               "MM/dd/yyyy HH:mm:ss",
                               System.Globalization.CultureInfo.InvariantCulture,
                               System.Globalization.DateTimeStyles.None,
                               out tradeTime))
                        {
                            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed to parse trade time for record Account({0}) Symbol({1}) Quantity({2}) Price({3}) TradeTime({4}) OrderId({5}) TradeId({6})", account, row.Symbol, row.Quantity, row.Price, row.TradeTime, orderId, tradeId);
                            Console.WriteLine("Can't parse trade time:  " + row.TradeTime);
                            continue;
                        }

                        // order time
                        if (!DateTime.TryParseExact(row.OrderTime,
                               "MM/dd/yyyy HH:mm:ss",
                               System.Globalization.CultureInfo.InvariantCulture,
                               System.Globalization.DateTimeStyles.None,
                               out orderTime))
                        {
                            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed to parse order time for record Account({0}) Symbol({1}) Quantity({2}) Price({3}) OrderTime({4}) OrderId({5}) TradeId({6})", account, row.Symbol, row.Quantity, row.Price, row.OrderTime, orderId, tradeId);
                            Console.WriteLine("Can't parse order time:  " + row.OrderTime);
                            continue;
                        }

                        // settlement date
                        if (!DateTime.TryParseExact(row.SettlementDate,
                               "MM/dd/yyyy",
                               System.Globalization.CultureInfo.InvariantCulture,
                               System.Globalization.DateTimeStyles.None,
                               out settlementDate))
                        {
                            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed to parse settlement date for record Account({0}) Symbol({1}) Quantity({2}) Price({3}) SettlementDate({4}) OrderId({5}) TradeId({6})", account, row.Symbol, row.Quantity, row.Price, row.SettlementDate, orderId, tradeId);
                            Console.WriteLine("Can't parse settlement date:  " + row.SettlementDate);
                            continue;
                        }

                        // ExchangeMIC
                        if (row.Destination == "DROP")
                        {
                            if (!Importer.AccountExchangeMap.TryGetValue(row.Account, out exchange))
                            {
                                exchange = Importer.GetExchangeMICfromSymbol(row.Symbol);
                            }
                        }
                        else
                        {
                            exchange = Importer.GetExchangeMIC(row.Destination);
                        }

                        if (exchange == "XXXX")
                        {
                            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed to find correct ExchangeMIC for {0} so defaulting to XXXX", row.Destination);
                        }

                        // Trade Flag
                        flag = row.Flag.Trim();
                        if (flag == String.Empty)
                        {
                            flag = row.FlagNative.Trim();
                            if (flag == String.Empty)
                            {
                                if (exchange == "XNYS")
                                    flag = "X";
                            }
                        }

                        // routedMIC and isAggressor values
                        Importer.GetFlagSettings(exchange, flag, out isAggressor, out routedMIC);
                       
                        // broker code
                        broker = null;
                        if (!String.IsNullOrEmpty(row.BrokerCode.Trim()))
                        {
                            var key = exchange + ":" + row.BrokerCode;
                            if(Importer.ExchangeBrokerCodes.Contains(key))
                                broker = row.BrokerCode;
                        }
                       
                        // insert trade into the database
                        int result = Importer.LiquidViewLinq.ImportTradeBySterlingSymbol(
                            Importer.HostName,
                            orderId,
                            tradeId,
                            exchange,
                            orderId,
                            tradeId,
                            account,
                            currency,
                            row.Symbol,
                            quantity,
                            price,
                            tradeTime,
                            row.Counterparty,
                            isAggressor,
                            false,
                            false,
                            broker,
                            Importer.ProcessName + " imported from " + filename,
                            false,
                            null,
                            routedMIC,
                            row.Flag,
                            null,
                            orderTime,
                            settlementDate);

                        if (result == 0)
                        {
                            ++countSuccess;
                        }
                        else
                        {
                            Console.WriteLine("Failed ImportTradeBySterlingSymbol Returned({7}) Account({0}) Symbol({1}) Quantity({2}) Price({3}) TradeTime({4}) OrderId({5}) TradeId({6})", row.Account, row.Symbol, row.Quantity, row.Price, row.TradeTime, row.OrderId, row.RecordId, result);
                            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed ImportTradeBySterlingSymbol Returned({7}) Account({0}) Symbol({1}) Quantity({2}) Price({3}) TradeTime({4}) OrderId({5}) TradeId({6})", row.Account, row.Symbol, row.Quantity, row.Price, row.TradeTime, row.OrderId, row.RecordId, result);
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("Failed to import trade!\r\n" + e);
                        JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed to import trade Account({0}) Symbol({1}) Quantity({2}) Price({3}) Date({4}) OrderId({5}) TradeId({6}) Exception({7})", row.Account, row.Symbol, row.Quantity, row.Price, row.TradeTime, row.OrderId, row.RecordId, e.Message);
                        continue;
                    }
                }
            }
            catch (Exception e)
            {
                JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Failed to extract data from csv format. Exception({0})", e);
                Console.WriteLine("Failed to extract data from csv format. Exception({0})", e);

                return false;
            }


            // archive the file
            try
            {
                if (!Importer.CheckDirectory(archiveDir))
                {
                    Console.WriteLine("Could not create directory " + archiveDir + ", " + filename + " not archived");
                    JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Could not create directory " + archiveDir + ", " + filename + " not archived");
                }
                else
                {
                    File.WriteAllText(archiveDir + filename, data, Encoding.ASCII);
                    Console.WriteLine("Successfully archived " + filename);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Unknow error when trying to archive '" + filename + "' to " + archiveDir +" Exception({0})", e.Message);
                JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(Importer.ProcessName, "Unknow error when trying to archive '" + filename + "' to " + archiveDir + " Exception({0})", e.Message);
            }

            return true;
        }


    }
}
